package com.thudojsc.canvasengine;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

import com.thudojsc.canvasengine.entity.Scene;
import com.thudojsc.canvasengine.touch.TouchEvent;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.View;
import android.view.View.OnTouchListener;

public class Engine implements Callback, OnTouchListener {

	private static final long TOUCHEVENT_INTERVAL_MILLISECONDS_DEFAULT = 20;
	
	
	private UpdateThread mUpdateThread;
	private boolean isFirstTime = true;
	
	private Scene mCurrentScene = null;
	
	private SurfaceHolder holder;
	
	private Queue<TouchEvent> mTouchEventQueue = new LinkedList<TouchEvent>();
	private Object mMutexForTouchEventQueuing = new Object();
	
	public void runWithScene(final Scene pScene) {
		synchronized (holder) {
			this.mCurrentScene = pScene;
		}
	}
	
	/*-----------------------------CALLBACK-------------------------*/
	private IEngineCallback mEngineCallback = null;
	
	public void setEngineCallback(final IEngineCallback pEngineCallback) {
		this.mEngineCallback = pEngineCallback;
	}
	
	public IEngineCallback getEngineCallback() {
		return this.mEngineCallback;
	}
	
	/*-----------------------CONTROL ENGINE--------------------------------*/
	public boolean isRunning() {
		return mUpdateThread.mRunning;
	}
	
	public void start() {
		if (!mUpdateThread.mRunning)
			mUpdateThread.mRunning = true;
	}
	
	public void stop() {
		if (mUpdateThread.mRunning)
			mUpdateThread.mRunning = false;
		
	}
	
	/*-----------------------SURFACE CALLBACK------------------------------*/
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		if (isFirstTime) {
			mEngineCallback.onCompleted(this);
			isFirstTime = false;
		}
		
		mEngineCallback.onDrawAreaSizeChanged(width, height);
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		this.holder = holder;
		mUpdateThread = new UpdateThread(holder, this);
		mUpdateThread.start();
		mUpdateThread.setRunning(true);
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		boolean retry = true;
		
		mUpdateThread.setRunning(false);
		
		while (retry) {
			try {
				mUpdateThread.join();
				retry = false;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/*--------------------TOUCH PROCESS----------------------*/
	protected void onProcessTouchEvent(final MotionEvent event) {
		final TouchEvent touchEvent = TouchEvent.obtain((int)event.getX(), (int)event.getY(), 0, event.getAction(), event);
		synchronized (mMutexForTouchEventQueuing) {
			mTouchEventQueue.add(touchEvent);
			
		}
	}
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		if (mUpdateThread.mRunning) {
			onProcessTouchEvent(event);
			//Tam dung de tranh tac nghen
			
			try {
				Thread.sleep(TOUCHEVENT_INTERVAL_MILLISECONDS_DEFAULT);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return true;
		}
		return false;
	}
	
	/*------------------------GAME HANDLER----------------------------*/
	/**
	 * Update engine
	 * @param deltaTime thoi gian (seconds) tinh tu lan update truoc
	 */
	protected void onUpdate(final float deltaTime) {
		if (mCurrentScene!= null) {
			synchronized (mMutexForTouchEventQueuing) {
				TouchEvent event = null;
				
				while ((event = mTouchEventQueue.poll()) != null) {
					mCurrentScene.onSceneTouchEvent(event);
				}
			}
			mCurrentScene.update(deltaTime);
		}
	}
	
	protected void onDraw(final Canvas pCanvas) {
		/* CLEAR SCREEN */
		Paint paint = new Paint();
		paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
		pCanvas.drawPaint(paint);
		paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
		
		if (mCurrentScene!= null)
			mCurrentScene.draw(pCanvas);
	}
	
	/*------------------------UPDATE THREAD---------------------------*/
	public class UpdateThread extends Thread {
		private SurfaceHolder mSurfaceHolder;
		private Engine mEngine;
		
		private boolean mRunning = false;
		
		private long mLastTime = 0;
		
		public UpdateThread(final SurfaceHolder holder, final Engine engine) {
			this.mSurfaceHolder = holder;
			this.mEngine = engine;
		}
		
		@Override
		public void run() {
			Canvas canvas = null;
			
			while (mRunning) {
				canvas = null;
				
				try {
					canvas = mSurfaceHolder.lockCanvas(null);
					
					synchronized (mSurfaceHolder) {
						/*Update Engine*/
						final long now = System.currentTimeMillis();
						
						if (mLastTime == 0)
							mEngine.onUpdate(0);
						else {	
							final float deltaTime = TimeUnit.MILLISECONDS.toSeconds(now - mLastTime);
							mEngine.onUpdate(deltaTime);
						}
						
						mLastTime = now;
						
						/*Draw*/
						mEngine.onDraw(canvas);
					}
					
				} finally {
					if (canvas != null) 
						mSurfaceHolder.unlockCanvasAndPost(canvas);
				}
			}
		}
		
		public void setRunning(final boolean pRunning) {
			this.mRunning = pRunning;
		}
		
		public boolean getRunning() {
			return this.mRunning;
		}
	}

	public interface IEngineCallback {
		public void onCompleted(final Engine pEngine);
		public void onDrawAreaSizeChanged(final int pAreaWidth, final int pAreaHeight);
	}
	
	
	
}
